package nqueens.algorithm;

import nqueens.chessStuff.ChessBoard;

public abstract class Algorithm implements Runnable {

    private ChessBoard cb; // the chessboard being solved
    private ChessBoard lastSolutionSolved; // the last chessboard that was solved with queens placed
    // A flag that represent whether or not error checking is being used.  This flag is ONLY for
    // Dr Ginat's and Dr Levitin's algoritms presently.
    private final boolean VALIDATE_SOLUTIONS;

    /**
     * Constructor that assigns a chessboard
     * @param cb The one and only chessboard to be solved
     */
    public Algorithm (ChessBoard cb) {
        this(cb,true);
    }

    /**
     * No argument constructor.  This will set the chessboard to null
     * (which will solve for all N such that N > 4 until interrupted)
     * and VALIDATE_SOLUTIONS to 'true'.
     */
    public Algorithm () {
        this(null, true);
    }

    /**
     * Constructor that sets the VALIDATE_SOLUTIONS flag to the variable specified and
     * will set the chessboard to null (which will solve for all N such that N > 4 until interrupted)
     * @param validateSolutions whether or not error checking will be used
     */
    public Algorithm (boolean validateSolutions) {
        this(null,validateSolutions);
    }

    /**
     * Constructor that specifies a specific case to solve, and a whether or not error checking will be used.
     * @param cb The one and only chessboard to be solved
     * @param validateSolutions Whether or not error checking is to be used
     */
    public Algorithm (ChessBoard cb, boolean validateSolutions) {
        VALIDATE_SOLUTIONS = validateSolutions;
        this.cb = cb;
    }

    /**
     * Helper method that is called to ensure a solution is valid.  It will exit the program
     * if an invalid solution is found.
     * @return whether or not a solution is valid
     */
    protected boolean validateSolution() {
        if (!getChessBoard().isSolved()) {
            System.err.println("******ALGORITHM "+ this.getClass().getSimpleName() +" THE BOARD IS NOT SOLVED FOR N = " + getChessBoard().getN() + "******");
            getChessBoard().displayBoard();
            System.exit(1);
            return false;
        }
        return true;
    }

    /**
     * Method for returning the flag for whether or not error checking is being used
     * @return the final variable 'VALIDATE_SOLUTIONS' dictating whether or not to use error checking
     */
    public boolean isValidatingSolutions () {
        return VALIDATE_SOLUTIONS;
    }

    /**
     * Returns the chessboard being solved.
     * @return The chessboard being solved.
     */
    public ChessBoard getChessBoard () {
        return cb;
    }

    /**
     * Sets the chessboard being solved
     * @param cb Set the chessboard that is presently being solved
     */
    public void setChessBoard(ChessBoard cb) {
        this.cb = cb;
    }

    /**
     * Return the last chessboard that was successfully solved.  This is synchronized as the main
     * thread will reach into this variable to retrive it from it's own seperate thread.
     * @return The last solution having been solved.
     */
    public synchronized ChessBoard getLastSolutionSolved() {
        return lastSolutionSolved;
    }


    /**
     * Sets the last chessboard that was successfully solved.  This is synchronized as the main
     * thread will reach into this variable to retrive it from it's own seperate thread.
     * @param lastSolutionSolved set the solution that was last solved.
     */
    public synchronized void setLastSolutionSolved(ChessBoard lastSolutionSolved) {
        this.lastSolutionSolved = lastSolutionSolved;
    }


    /**
     *
     * @param q A list queens with their corresponding index based upon a single linear array representation     *
     * @return The chessboard where the placement is as referenced per the array passed as argument
     */
    public ChessBoard convertToChessBoard (int [] q) throws InterruptedException {
        int N = q.length;
        ChessBoard tempBoard = new ChessBoard(N, isValidatingSolutions());
        for (int rows = 0; rows < N; rows++) {
            for (int columns = 0; columns < N; columns++) {
                if (q[rows] == columns) {
                    tempBoard.placeQueen(rows, columns);
                }
            }
        }
        return tempBoard;
    }

    /**
     * Abstract method to be overridden by the actual algorithms.  This will be the method doing the actual solving.
     * @param n the problem size to solve
     * @throws InterruptedException Throws up an interruption to bubble it back to the run method
     */
    protected abstract void solve(int n) throws InterruptedException;

    @Override
    public void run() {
        // start at 4, and increment n until I get interrupted
        int n = 4;
        // endless loop with a check for overflowing the Max Value && a check for thread interrupts
        for (n = 4; n < Integer.MAX_VALUE && !Thread.currentThread().isInterrupted(); n++) {
            try {
                solve(n);
            } catch (InterruptedException ex) {
                break;
            }
        }
    }

    

}
